﻿#include <occmesh/mesh/mesh_data.h>

IMPLEMENT_STANDARD_RTTIEXT(OCCMesh_Data, MeshVS_DataSource)

OCCMesh_Data::OCCMesh_Data(Handle(Poly_Triangulation) plyTri) : m_plyTri(plyTri)
{
    // 将三角化结果转换为网格数据
    if (!plyTri.IsNull())
    {
        const Standard_Integer aNbNodes = plyTri->NbNodes();

        // 使用 2 维数组 m_nodeCoords 保存节点坐标，m_nodes 保存节点索引
        m_nodeCoords = new TColStd_HArray2OfReal(1, aNbNodes, 1, 3);
        for (Standard_Integer i = 1; i <= aNbNodes; i++)
        {
            m_nodes.Add(i);
            gp_Pnt xyz = plyTri->Node(i);

            m_nodeCoords->SetValue(i, 1, xyz.X());
            m_nodeCoords->SetValue(i, 2, xyz.Y());
            m_nodeCoords->SetValue(i, 3, xyz.Z());
        }

        const Standard_Integer aNbTris = plyTri->NbTriangles();

        // 保存三角面法向坐标和顶点索引
        m_elemNormals = new TColStd_HArray2OfReal(1, aNbTris, 1, 3);
        m_elemNodes = new TColStd_HArray2OfInteger(1, aNbTris, 1, 3);
        for (Standard_Integer i = 1; i <= aNbTris; i++)
        {
            // 保存三角面索引
            m_elements.Add(i);

            const Poly_Triangle aTri = plyTri->Triangle(i);

            // 获得三角面顶点索引
            Standard_Integer V[3];
            aTri.Get(V[0], V[1], V[2]);

            const gp_Pnt aP1 = plyTri->Node(V[0]);
            const gp_Pnt aP2 = plyTri->Node(V[1]);
            const gp_Pnt aP3 = plyTri->Node(V[2]);

            gp_Vec aV1(aP1, aP2);
            gp_Vec aV2(aP2, aP3);

            // 计算法向
            gp_Vec aN = aV1.Crossed(aV2);
            if (aN.SquareMagnitude() > Precision::SquareConfusion())
                aN.Normalize();
            else
                aN.SetCoord(0.0, 0.0, 0.0);

            for (Standard_Integer j = 0; j < 3; j++)
                m_elemNodes->SetValue(i, j + 1, V[j]);

            m_elemNormals->SetValue(i, 1, aN.X());
            m_elemNormals->SetValue(i, 2, aN.Y());
            m_elemNormals->SetValue(i, 3, aN.Z());
        }
    }
}

Standard_Boolean OCCMesh_Data::GetGeom(const Standard_Integer ID, const Standard_Boolean IsElement,
                                       TColStd_Array1OfReal &Coords, Standard_Integer &NbNodes,
                                       MeshVS_EntityType &Type) const
{
    if (m_nodes.Extent() == 0)
        return Standard_False;

    if (IsElement)
    {
        // 根据 ID 获得三角面
        if (ID >= 1 && ID <= m_elements.Extent())
        {
            Type = MeshVS_ET_Face;
            NbNodes = 3;

            for (Standard_Integer i = 1, k = 1; i <= 3; i++)
            {
                Standard_Integer IdxNode = m_elemNodes->Value(ID, i);
                for (Standard_Integer j = 1; j <= 3; j++, k++)
                    Coords(k) = m_nodeCoords->Value(IdxNode, j);
            }

            return Standard_True;
        }
        else
            return Standard_False;
    }
    else if (ID >= 1 && ID <= m_nodes.Extent())
    {
        // 根据 ID 获得顶点
        Type = MeshVS_ET_Node;
        NbNodes = 1;

        Coords(1) = m_nodeCoords->Value(ID, 1);
        Coords(2) = m_nodeCoords->Value(ID, 2);
        Coords(3) = m_nodeCoords->Value(ID, 3);
        return Standard_True;
    }
    else
        return Standard_False;
}

Standard_Boolean OCCMesh_Data::GetGeomType(const Standard_Integer theID, const Standard_Boolean IsElement,
                                           MeshVS_EntityType &theType) const
{
    if (m_nodes.Extent() == 0)
        return Standard_False;

    if (IsElement)
    {
        if (theID >= 1 && theID <= m_elements.Extent())
        {
            theType = MeshVS_ET_Face;
            return Standard_True;
        }
    }
    else if (theID >= 1 && theID <= m_nodes.Extent())
    {
        theType = MeshVS_ET_Node;
        return Standard_True;
    }
    return Standard_False;
}

Standard_Address OCCMesh_Data::GetAddr(const Standard_Integer /*ID*/, const Standard_Boolean /*IsElement*/) const
{
    return NULL;
}

Standard_Boolean OCCMesh_Data::GetNodesByElement(const Standard_Integer ID, TColStd_Array1OfInteger &theNodeIDs,
                                                 Standard_Integer & /*theNbNodes*/) const
{
    if (m_nodes.Extent() == 0)
        return Standard_False;

    // 根据 ID 获得三角面的顶点索引
    if (ID >= 1 && ID <= m_elements.Extent() && theNodeIDs.Length() >= 3)
    {
        Standard_Integer aLow = theNodeIDs.Lower();
        theNodeIDs(aLow) = m_elemNodes->Value(ID, 1);
        theNodeIDs(aLow + 1) = m_elemNodes->Value(ID, 2);
        theNodeIDs(aLow + 2) = m_elemNodes->Value(ID, 3);
        return Standard_True;
    }
    return Standard_False;
}

Standard_Boolean OCCMesh_Data::GetNormal(const Standard_Integer Id, const Standard_Integer Max, Standard_Real &nx,
                                         Standard_Real &ny, Standard_Real &nz) const
{
    if (m_nodes.Extent() == 0)
        return Standard_False;

    // 获得法向
    if (Id >= 1 && Id <= m_elements.Extent() && Max >= 3)
    {
        nx = m_elemNormals->Value(Id, 1);
        ny = m_elemNormals->Value(Id, 2);
        nz = m_elemNormals->Value(Id, 3);
        return Standard_True;
    }
    else
        return Standard_False;
}

const TColStd_PackedMapOfInteger &OCCMesh_Data::GetAllNodes() const
{
    return m_nodes;
}

const TColStd_PackedMapOfInteger &OCCMesh_Data::GetAllElements() const
{
    return m_elements;
}

Handle(Poly_Triangulation) OCCMesh_Data::GetTriangulation() const
{
    return m_plyTri;
}